import com.intellij.database.model.DasTable
import com.intellij.database.model.DasObject
import com.intellij.database.model.ObjectKind
import com.intellij.database.util.DasUtil
import com.intellij.openapi.project.Project

import com.intellij.database.view.generators.Files

/**
 * https://github.com/novotnyr/mybatis-idea-scripted-extension
 */
public class MybatisXmlMapperGenerator {

  private String getProjectName(dir) {
    return "pontiac"
  }

  public void start(Settings settings, Iterable<DasObject> selection, File targetDirectory) {
    def projectName = getProjectName(targetDirectory.getPath())
    settings.mapperNamespace = "com.pajk.${projectName}.core.dal.mapper"
    settings.entityPackage = "com.pajk.${projectName}.core.dal.model"
    selection.filter {
      it instanceof DasTable && it.getKind() == ObjectKind.TABLE
    }.find().each { DasTable table ->
      GString mapperDefinition = emitMapper(table, settings)
      writeToDirectory(targetDirectory, mapperDefinition, table, settings)
    }
  }


  private void writeToDirectory(File directory, GString mapperDefinition, DasTable table, Settings settings) {
    String fileName = toEntityClassName(table, settings) + "Mapper.xml";

    fileName = fileName.replaceFirst("Pontiac","")
    new File(directory, fileName).write(mapperDefinition)
  }

  private GString emitMapper(DasTable table, Settings settings) {
    def namespace = settings.mapperNamespace + "." + toEntityClassName(table, settings) + "Mapper"

    namespace = namespace.replaceFirst("Pontiac","")

    GString mapperDefinition = emitResultMap(table, settings)
    mapperDefinition += emitBaseColumns(table, settings)
    mapperDefinition += emitSetColumn(table, settings)
    mapperDefinition += emitPageQueryParam(table, settings)
    mapperDefinition += emitInsert(table, settings)
    mapperDefinition += emitUpdate(table, settings)
    mapperDefinition += emitDelete(table, settings)
    mapperDefinition += emitFindById(table, settings)
    mapperDefinition += emitPageQueryCount(table, settings)
    mapperDefinition += emitPageQuery(table, settings)


    """<?xml version="1.0" encoding="UTF-8" ?>\n<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">\n<mapper namespace="${namespace}">\n${mapperDefinition}</mapper>"""
  }

  private GString emitBaseColumns(DasTable table, Settings settings) {
    def columns = DasUtil.getColumns(table).collect { column ->"`${column.name}`"}.join(',')
    """\t<sql id="Base_Column_List">\n\t\t${columns}\n\t</sql>\n\n"""
  }

  private GString emitPageQueryParam(DasTable table, Settings settings) {
    def test = ""
    """\t<sql id="Page_Query">\n\t\t<where>${test}\n\t\t</where>\n\t</sql>\n\n"""
  }

  private GString emitPageQueryCount(DasTable table, Settings settings) {
    """\t<select id="countPageQuery" resultType="java.lang.Integer">\n\t\tSELECT COUNT(1)\n\t\tFROM ${table.name}\n\t\t<include refid="Page_Query"/>\n\t</select>\n\n"""
  }

  private GString emitPageQuery(DasTable table, Settings settings) {
    """\t<select id="pageQuery" resultMap="ResultMap">\n\t\tSELECT\n\t\t<include refid="Base_Column_List"/>\n\t\tFROM ${table.name}\n\t\t<include refid="Page_Query"/>\n\t\tLIMIT #{offset},#{pageSize}\n\t</select>\n\n"""
  }

  private GString emitDelete(DasTable table, Settings settings) {
    """\t<delete id="delete">\n\t\tdelete from ${table.name}\n\t\twhere id = #{id}\n\t</delete>\n\n """
  }


  private GString emitFindById(DasTable table, Settings settings) {
    """\t<select id="getById" resultMap="ResultMap">\n\t\tSELECT\n\t\t<include refid="Base_Column_List"/>\n\t\tFROM `${table.name}`\n\t\tWHERE `id`  = #{id}\n\t</select>\n\n"""
  }

  private GString emitSetColumn(DasTable table, Settings settings) {
    def mappings =  DasUtil
            .getColumns(table)
            .collect { column ->
      def propertyName = toPropertyName(column.name);
      if ("gmt_modified".equals(column.name)){
        """\t\t\tgmt_modified = now()"""
      }else if(DasUtil.isPrimary(column)){
        """"""
      }else {
        """\t\t\t<if test="${propertyName} != null">${column.name}=#{${propertyName}},</if>"""
      }
    }.join('\n')

    """\t<sql id="Set_Column">\n\t\t<set>\n${mappings}\n\t\t</set>\n\t</sql>\n\n"""
  }

  private GString emitResultMap(DasTable table, Settings settings) {
    def resultType = settings.entityPackage + "." + toEntityClassName(table, settings)+"DO"

    resultType = resultType.replaceFirst("Pontiac","")

    def mappings =  DasUtil
            .getColumns(table)
            .collect { column ->
      """\t\t<result column="${column.name}" property="${toPropertyName(column.name)}" />"""
    }
    .join('\n')

    """\t<resultMap id="ResultMap" type="${resultType}">\n${mappings}\n\t</resultMap>\n\n"""
  }

  private GString emitInsert(DasTable table, Settings settings) {
    def resultType = settings.entityPackage + "." + toEntityClassName(table, settings)+"DO";
    resultType = resultType.replaceFirst("Pontiac","")
    """\t<insert id="insert" useGeneratedKeys="true" keyProperty="id" parameterType="${resultType}">\n\t\tinsert into ${table.name} <include refid="Set_Column"/>\n\t</insert>\n\n"""
  }

  private GString emitUpdate(DasTable table, Settings settings) {
    def resultType = settings.entityPackage + "." + toEntityClassName(table, settings)+"DO";
    resultType = resultType.replaceFirst("Pontiac","")
    """\t<update id="update" parameterType="${resultType}">\n\t\tUPDATE ${table.name}\n\t\t<include refid="Set_Column"/>\n\t\tWHERE id = #{id}\n\t</update>\n\n"""
  }

  private CharSequence toCamelCase(CharSequence input) {
    return input
            .split('_')
            .collect{ it.capitalize() }
            .join('')
  }


  private CharSequence toPropertyName(CharSequence input) {
    def words = input
            .split('_')
            .collect { it.capitalize() }
    if (words.size() > 1) {
      return (words[0].toLowerCase() + words[1..-1].join("") )
    } else {
      return words[0].toLowerCase();
    }
  }

  private CharSequence toJavaClassName(DasTable table) {
    return toCamelCase(table.name)
  }

  private CharSequence toEntityClassName(DasTable table, Settings settings) {
    if(settings.entityClassName == null || settings.entityClassName.isEmpty()) {
      return toJavaClassName(table)
    } else {
      return settings.entityClassName
    }
  }
}


class Settings {
  String mapperNamespace
  String entityPackage
  String entityClassName
}

import com.intellij.openapi.application.ApplicationManager

ApplicationManager.getApplication().invokeLater {
  FILES.chooseDirectoryAndSave("Choose directory", "Choose the directory for generated files") { directory ->
    def settings = new Settings()
    settings.mapperNamespace = null
    settings.entityPackage = null
    settings.entityClassName = null

    MybatisXmlMapperGenerator generator = new MybatisXmlMapperGenerator()
    generator.start(settings, SELECTION, directory)
  }
}